#include "DXUT.h"
#include "Entity.h"
#include "../Physics/Collision.h"
#include "../World.h"

Entity::Entity(const D3DXVECTOR3& pos) {
	m_Position			= pos;
	m_Physics			= true;
#ifdef RENDER
	m_pVertexBuffer11	= NULL;
	m_pIndexBuffer11	= NULL;
	m_pcbVSPerObject11	= NULL;
#endif
	m_Velocity			= D3DXVECTOR3(0.0,0.0,0.0);
	m_Rotation			= D3DXVECTOR3(0.0,0.0,0.0);

	m_BoundingBox		= D3DXAABB(D3DXVECTOR3(-0.3f, -0.3f, -0.3f), D3DXVECTOR3( 0.3f,  0.3f,  0.3f));
}

Entity::Entity(const D3DXVECTOR3& pos, const D3DXAABB& aabb) {
	m_Position			= pos;
	m_Physics			= true;
#ifdef RENDER
	m_pVertexBuffer11	= NULL;
	m_pIndexBuffer11	= NULL;
	m_pcbVSPerObject11	= NULL;
#endif
	m_Velocity			= D3DXVECTOR3(0.0,0.0,0.0);
	m_Rotation			= D3DXVECTOR3(0.0,0.0,0.0);

	m_BoundingBox		= aabb;
};

D3DXVECTOR3 Entity::getPosition() {
	return m_Position;
}

/* Called in irregular patterns to perform an EntityUpdate
   fElapsedTime defines the time passed in sec
   will call onEntityUpdate for each 50ms passed*/
void Entity::OnFrameMove( EntityWorld * pWorld, double fTime, float fElapsedTime) {

	float Speed = fElapsedTime;
	m_Grounded  = false;
	D3DXVECTOR3 dir = m_Velocity * Speed;

	if (m_Physics) {
		D3DXAABB aabb = m_BoundingBox + m_Position + dir;

		Block blocks[8];
		bool collides[8];
		INTVECTOR3 pos[8];
		if (!collision(pWorld, aabb, blocks, pos, collides)) {
			/* No Collision */
			m_Position += dir;
			goto last; 
		}

		aabb -= dir;

		int loopcount = 0;
		while(fElapsedTime > 1e-10)
		{
			loopcount++;
			if(loopcount >= 100)
			{
				fElapsedTime = 0;
				break;
			}

			int nearest_collided = -1;
			float nearest_dtime = fElapsedTime;
			int nearest_boxindex = -1;

			for (int i = 0; i < 8; i++) {
				if (!collides[i]) continue;

				if ((blocks[i].status & SOLID) == 0) continue;
				/* BLOCK AABB */
				D3DXAABB Saabb(
					D3DXVECTOR3(-0.5f + pos[i].x, -0.5f + pos[i].y, -0.5f + pos[i].z),
					D3DXVECTOR3( 0.5f + pos[i].x,  0.5f + pos[i].y,  0.5f + pos[i].z)
					);

				float dtime_tmp;
				int collided = axisAlignedCollision(Saabb, aabb, m_Velocity, Speed * 20.0f, dtime_tmp);

				if(collided == -1 || dtime_tmp >= nearest_dtime) 
					continue;


				nearest_dtime = dtime_tmp;
				nearest_collided = collided;
				nearest_boxindex = i;
			}

			if(nearest_collided == -1)
			{
				// No collision with any collision box.
				m_Position += m_Velocity * fElapsedTime;
				fElapsedTime = 0;  // Set to 0 to avoid "infinite" loop due to small FP numbers
			}
			else
			{
				/*
				// Otherwise, a collision occurred.

				const aabb3f& cbox = cboxes[nearest_boxindex];

				// Check for stairs.
				bool step_up = (nearest_collided != 1) && // must not be Y direction
				(movingbox.MinEdge.Y < cbox.MaxEdge.Y) &&
				(movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) &&
				(!wouldCollideWithCeiling(cboxes, movingbox,
				cbox.MaxEdge.Y - movingbox.MinEdge.Y,
				d));

				// Get bounce multiplier
				bool bouncy = (bouncy_values[nearest_boxindex] >= 1);
				float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;
				*/
				// Move to the point of collision and reduce dtime by nearest_dtime
				if(nearest_dtime < 0)
				{
					// Handle negative nearest_dtime (can be caused by the d allowance)
					//if(!step_up)
					{
						if(nearest_collided == 0)
							m_Position.x += m_Velocity.x * nearest_dtime;
						if(nearest_collided == 1)
							m_Position.y += m_Velocity.y * nearest_dtime;
						if(nearest_collided == 2)
							m_Position.z += m_Velocity.z * nearest_dtime;
					}	

				}
				else
				{
					m_Position += m_Velocity * nearest_dtime;
					fElapsedTime -= nearest_dtime;
				}

				if(nearest_collided == 0) // X
				{
					m_Velocity.x = 0;
				}
				else if(nearest_collided == 1) // Y
				{
					m_Velocity.y  = 0;
				}
				else if(nearest_collided == 2) // Z
				{
					m_Velocity.z = 0;
				}
				/*			
				bool is_collision = true;
				if(is_unloaded[nearest_boxindex])
				is_collision = false;

				CollisionInfo info;
				info.type = COLLISION_NODE;
				info.node_p = node_positions[nearest_boxindex];
				info.bouncy = bouncy;
				info.old_speed = speed_f;

				// Set the speed component that caused the collision to zero
				if(step_up)
				{
				// Special case: Handle stairs
				is_step_up[nearest_boxindex] = true;
				is_collision = false;
				}
				else if(nearest_collided == 0) // X
				{
				if(fabs(speed_f.X) > BS*3)
				speed_f.X *= bounce;
				else
				speed_f.X = 0;
				result.collides = true;
				result.collides_xz = true;
				}
				else if(nearest_collided == 1) // Y
				{
				if(fabs(speed_f.Y) > BS*3)
				speed_f.Y *= bounce;
				else
				speed_f.Y = 0;
				result.collides = true;
				}
				else if(nearest_collided == 2) // Z
				{
				if(fabs(speed_f.Z) > BS*3)
				speed_f.Z *= bounce;
				else
				speed_f.Z = 0;
				result.collides = true;
				result.collides_xz = true;
				}

				info.new_speed = speed_f;
				if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
				is_collision = false;

				if(is_collision){
				result.collisions.push_back(info);
				}*/
			}
		}
		for (int i = 0; i < 8; i++) {
			if (!collides[i]) continue;

			/* BLOCK AABB */
			D3DXAABB Saabb(
				D3DXVECTOR3(-0.5f + pos[i].x, -0.5f + pos[i].y, -0.5f + pos[i].z),
				D3DXVECTOR3( 0.5f + pos[i].x,  0.5f + pos[i].y,  0.5f + pos[i].z)
				);

			/*
			See if the object is touching ground.

			Object touches ground if object's minimum Y is near node's
			maximum Y and object's X-Z-area overlaps with the node's
			X-Z-area.

			Use 0.15*BS so that it is easier to get on a node.
			*/
			if(
				Saabb.Max.x-0.01 > aabb.Min.x &&
				Saabb.Min.x+0.01 < aabb.Max.x &&
				Saabb.Max.z-0.01 > aabb.Min.z &&
				Saabb.Min.z+0.01 < aabb.Max.z
				){
					/*
					if(is_step_up[boxindex])
					{
					pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
					box = box_0;
					box.MinEdge += pos_f;
					box.MaxEdge += pos_f;
					}*/
					if(fabs(Saabb.Max.y-aabb.Min.y) < 0.05)
					{/*
					 result.touching_ground = true;
					 if(is_unloaded[boxindex])
					 result.standing_on_unloaded = true;*/
						m_Grounded = true;

					}
			}
		}
	}
last:
	m_Velocity.x *= 1.0f - (20.0f * Speed);
//	m_Velocity.y -= m_Velocity.y * Speed * (10.0f);
	m_Velocity.z *= 1.0f - (20.0f * Speed);
}

#ifdef RENDER

void Entity::OnD3D11FrameRender( const D3DXFRUSTUM frustum, EntityWorld * pWorld, ID3D11DeviceContext* pd3dImmediateContext, double fTime, float fElapsedTime) {
	if (AABBInFrustum(m_BoundingBox.Min + m_Position, m_BoundingBox.Max + m_Position, frustum)) {
		D3DXMATRIX mMat;
		D3DXMatrixTranslation(&mMat, m_Position.x, m_Position.y, m_Position.z );
		D3DXMATRIX mRot;
		D3DXMatrixRotationY(&mRot, m_Rotation.y);

		mMat = mRot * mMat;

		CB_VS_PER_OBJECT			cb0;
		D3D11_MAPPED_SUBRESOURCE	cbMapped;
		D3DXMatrixTranspose( &cb0.mWorld, &mMat );
		Block block = pWorld->getBlock(m_Position);
		cb0.lGloballight = D3DXVECTOR4(g_LightLevels[block.skyLight], g_LightLevels[block.blockLight], 0.0f, 0.0f);

		if (pd3dImmediateContext->Map(m_pcbVSPerObject11, 0, D3D11_MAP_WRITE_DISCARD, 0, &cbMapped) == S_OK) {
			CopyMemory(cbMapped.pData, &cb0, sizeof( CB_VS_PER_OBJECT ));
			pd3dImmediateContext->Unmap(m_pcbVSPerObject11, 0);
		}
		//	pd3dImmediateContext->VSSetConstantBuffers( 1, 1, &m_pcbVSPerObject11 );

		UINT stride = sizeof( BlockVertex );
		UINT offset = 0;

		pd3dImmediateContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer11, &stride, &offset );
		pd3dImmediateContext->IASetIndexBuffer( m_pIndexBuffer11, DXGI_FORMAT_R16_UINT, 0 );
		pd3dImmediateContext->DrawIndexed( m_indexSize, 0, 0 );
	}
}


void Entity::OnD3D11CreateDevice( ID3D11Device* pd3dDevice ) {
	BlockVertex *   vertices	= NULL;
	WORD			vs			= 0;
	WORD *			indices		= NULL;
	WORD			is			= 0;

	CACTUS_BLOCK.GenerateIndexed(&vertices, &vs, &indices, &is);

	D3DXMATRIX mMat;
	D3DXMatrixScaling(&mMat, 0.3f, 0.3f, 0.3f);
	for (int i = 0; i < vs; i++)
		D3DXVec3TransformCoord(&vertices[i].Pos, &vertices[i].Pos, &mMat);

	D3D11_BUFFER_DESC bd;
	ZeroMemory( &bd, sizeof(bd) );
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( BlockVertex ) * vs;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory( &InitData, sizeof(InitData) );
	InitData.pSysMem = vertices;
	pd3dDevice->CreateBuffer( &bd, &InitData, &m_pVertexBuffer11 );

	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( WORD ) * is;        // 36 vertices needed for 12 triangles in a triangle list
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = 0;
	InitData.pSysMem = indices;
	pd3dDevice->CreateBuffer( &bd, &InitData, &m_pIndexBuffer11 );

	m_indexSize = is;

	free(vertices);
	free(indices);
}

void Entity::OnD3D11DestroyDevice( ) {
	SAFE_RELEASE(m_pVertexBuffer11);
	SAFE_RELEASE(m_pIndexBuffer11);
}

ID3D11Buffer* Entity::m_pcbVSPerObject11 = NULL;
void Entity::InitDevice( ID3D11Buffer* cb  ) {
	m_pcbVSPerObject11 = cb;
};


#endif